Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations.
1<script lang="ts">
2 import {Migrator, PlcOps} from '@pds-moover/moover'
3 import {resolve} from '$app/paths'
4 import RotationKeyDisplay from '$lib/components/RotationKeyDisplay.svelte';
5 import type {RotationKeyType} from '$lib/types';
6 import {env} from '$env/dynamic/public';
7
8 let {migrator, newHandle}: { migrator: Migrator, newHandle: string } = $props();
9
10 //UI State
11 let errorMessage: null | string = $state(null);
12 let done = $state(false);
13 let plcStatus: null | string = $state(null);
14 let showAdvancedPlcOptions = $state(false);
15 let backupSignupMessage: null | string = $state(null);
16 let backupSignupError: null | string = $state(null);
17
18 //Input State
19 let createANewRotationKey = $state(false);
20 let signupForBackups = $state(false);
21 let plcToken = $state('');
22 let rotationKeys = $state(['', '', '', '']);
23 let newlyCreatedRotationKey: RotationKeyType | null = $state(null);
24
25
26 async function signPlcOperation(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
27 event.preventDefault();
28 try {
29 plcStatus = 'Signing PLC operation...';
30 backupSignupMessage = null;
31 backupSignupError = null;
32 // Build an additional rotation keys list (user-provided and/or newly created)
33 const additionalRotationKeysToAdd: string[] = [];
34 // Generate a new rotation key if requested
35 if (createANewRotationKey) {
36
37 let plcOps = new PlcOps();
38 const created = await plcOps.createANewSecp256k1();
39 newlyCreatedRotationKey = created; // { publicKey, privateKey }
40 // Reserve the first slot for the newly created key (will appear above the PDS rotation key)
41 additionalRotationKeysToAdd.push(created.publicKey);
42 }
43 // Append any manually entered rotation keys (non-empty)
44 //TODO idk about this i need to look at it again
45 for (let i = 0; i < rotationKeys.length; i++) {
46 const k = (rotationKeys[i] || '').trim();
47 if (k) {
48 additionalRotationKeysToAdd.push(k);
49 }
50 }
51
52 //TODO nervous about this state
53 await migrator.signPlcOperation(plcToken, additionalRotationKeysToAdd);
54 plcStatus = 'PLC operation signed successfully! Your account has been MOOved to the new PDS.';
55 done = true;
56
57 if (signupForBackups) {
58 try {
59 backupSignupMessage = 'Signing you up for automated backups...';
60 //TODO nervous about this state
61 await migrator.signUpForBackupsFromMigration(`did:web:${env.PUBLIC_XRPC_BASE}`);
62 backupSignupMessage = 'Signed up for automated backups successfully.';
63 } catch (e) {
64 console.error(e);
65 //@ts-expect-error: There is a e.message, or at least a check for it
66 backupSignupError = e?.message || 'Failed to sign you up for automated backups.';
67 }
68 }
69 } catch (error) {
70 //@ts-expect-error: There is a message
71 errorMessage = error.message;
72 console.error(error);
73 }
74 }
75
76</script>
77
78
79<div class="section">
80 <form onsubmit="{signPlcOperation}">
81 {#if !done}
82 <div>
83 <h2>Please check your email attached to your previous account for a PLC token to enter below</h2>
84 <div class="form-group">
85 <label for="plc-token">PLC Token:</label>
86 <input type="text" id="plc-token" name="plc-token" bind:value={plcToken} required>
87 </div>
88 <p style="text-align: left">
89 Please check the boxes below if you would like to add a Rotation Key to your account and to sign up for PDS MOOver's free backup service.
90 With a Rotation Key and backups if your new PDS ever goes down
91 you can recover your account and it's data. This is not required but highly recommended.</p>
92 <div class="form-group">
93 <label for="rotation-key" class="moove-checkbox-label">
94 <input bind:checked={createANewRotationKey} type="checkbox" id="rotation-key"
95 name="rotation-key">
96 <span>Create and add a new Rotation Key</span>
97 </label>
98 </div>
99 <div class="form-group">
100 <label for="backups-signup" class="moove-checkbox-label">
101 <input bind:checked={signupForBackups} type="checkbox" id="backups-signup"
102 name="backups-signup">
103 <span>Signup for automated account backups</span>
104 </label>
105 </div>
106 <div class="form-group">
107 <button type="button" id="plc-advance"
108 onclick={() => showAdvancedPlcOptions = !showAdvancedPlcOptions}>Add
109 Additional Rotation Keys
110 </button>
111 </div>
112 {#if showAdvancedPlcOptions}
113 <div class="section" style="padding-bottom: 10px;">
114 <div style="text-align: left;">
115 You can pick up to 4 rotation keys to your PLC document. These will appear above your new
116 PDS
117 rotation key so you can recover your account in the event of an adversarial take over from a
118 rogue PDS
119 </div>
120 <div class="form-group" style="margin-top: 10px;">
121 <label for="rotation-key-1">Rotation key 1</label>
122 <input type="text" id="rotation-key-1" name="rotation-key-1"
123 bind:value={rotationKeys[0]}
124 disabled={createANewRotationKey}
125 placeholder={createANewRotationKey ? 'reserved for the newly created rotation key' : ''}>
126 </div>
127 <div class="form-group">
128 <label for="rotation-key-2">Rotation key 2</label>
129 <input type="text" id="rotation-key-2" name="rotation-key-2" bind:value={rotationKeys[1]}>
130 </div>
131 <div class="form-group">
132 <label for="rotation-key-3">Rotation key 3</label>
133 <input type="text" id="rotation-key-3" name="rotation-key-3" bind:value={rotationKeys[2]}>
134 </div>
135 <div class="form-group">
136 <label for="rotation-key-4">Rotation key 4</label>
137 <input type="text" id="rotation-key-4" name="rotation-key-4" bind:value={rotationKeys[3]}>
138 </div>
139 </div>
140 {/if}
141 </div>
142 {/if}
143 {#if errorMessage}
144 <div class="error-message">{errorMessage}</div>
145 {/if}
146
147 {#if done && createANewRotationKey && newlyCreatedRotationKey}
148 <div>
149 <RotationKeyDisplay handle={newHandle} rotationKey={newlyCreatedRotationKey}/>
150 </div>
151 {/if}
152
153 {#if !done && plcStatus}
154 <div class="status-message">{plcStatus}</div>
155 {/if}
156
157 {#if signupForBackups && backupSignupMessage}
158 <div>
159 <div class="status-message">{backupSignupMessage}</div>
160 {#if backupSignupError}
161 <div class="error-message">{backupSignupError}</div>
162 {/if}
163
164 </div>
165 {/if}
166
167 {#if done}
168 <div class="status-message">Congratulations! You have MOOved to a new PDS! Remember to use
169 your new PDS URL under "Hosting provider" when logging in on Bluesky. If you cannot login or see "Your account is deactivated" please follow the directions here
170 <a href={resolve('/info#cant-login')}>here.</a></div>
171 {:else }
172 <div>
173 <button type="submit">Sign the papers</button>
174 </div>
175 {/if}
176
177
178 </form>
179</div>